<template>
  <div>
    <div>
      <button @click="add">add first</button>
      &nbsp;
      <button @click="remove">remove first</button>
      &nbsp;
      <button @click="sort">sort</button>
    </div>

    <h2>使用id作为key</h2>
    <ul>
      <li v-for="p in persons" :key="p.id">
        {{p.id}}--{{p.name}}--{{p.age}}--<input/>
      </li>
    </ul>

    <h2>使用index作为key</h2>
    <ul>
      <li v-for="(p, index) in persons" :key="index">
        {{p.id}}--{{p.name}}--{{p.age}}--<input/>
      </li>
    </ul>
  </div>
</template>

<script>
/*
  面试题:
    1). react/vue中的key的作用/内部原理
    2). 为什么列表的key尽量不要用index
  1. 虚拟DOM的key的作用?
    1). 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用
    2). 详细的说: 当列表数组中的数据发生变化生成新的虚拟DOM后, React/Vue进行新旧虚拟DOM的diff比较
        a. 有一个对应的key
            item数据没变, 直接使用原来的真实DOM
            item数据变了, 对原来的真实DOM进行数据更新
        b. 没有一个对应key
            根据item数据创建新的真实DOM显示
  2. key为index的问题
    1). 添加/删除/排序 => 产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低
    2). 如果item界面还有输入框 => 复用了一个错误真实DOM ==> 界面有问题
    注意: 如果不存在添加/删除/排序操作, 用index没有问题
  3. 解决:
    使用item数据的标识数据作为key, 比如id属性值

  数据:
    [
      {id: 1, name: 'Tom', age: 13},    
      {id: 2, name: 'Jack', age: 12}
    ]
  index作为key
    {id: 1, name: 'Tom'} => vLi0 ==> <li>Tom</li>
    {id: 2, name: 'Jack'} => vLi1 ==> <li>Jack</li>

    [
      {id: 3, name: 'bob', age: 15}
      {id: 1, name: 'Tom', age: 13},
      {id: 2, name: 'Jack', age: 12}
    ]
    {id: 3, name: 'bob'} => vLi0 => 复用 <li>Tom</li>, 更新这个真实DOM
    {id: 1, name: 'Tom'} => vLi1 => 复用 <li>Jack</li>, 更新这个真实DOM
    {id: 2, name: 'Jack'} => vLi2 => 新建真实DOM, 并填入数据


  id作为key
    {id: 1, name: 'Tom'} => vLi1 ==> <li>Tom</li>
    {id: 2, name: 'Jack'} => vLi2 ==> <li>Jack</li>

    {id: 3, name: 'bob'} => vLi3 => 新建真实DOM, 并填入数据
    {id: 1, name: 'Tom'} => vLi1 => 复用 <li>Tom</li>, 不需要更新
    {id: 2, name: 'Jack'} => vLi2 => 复用 <li>Jack</li>, 不需要更新

  总结:
    下标作为key的问题?
        至少效率的问题?  复用了其它数据对应的真实DOM(key变化了)
        可能会有效果的问题?  如果每行都一个输入框且输入的数据(key变化了, 复用了错误的真实DOM)
    好的处理: 要保证数据key的稳定性 ==> 同一个数据的key后面不要再发生变化, 即使内部数据发生了变化
        下标是不稳定的, 而id比较稳定
  */
export default {
  name: 'Vdom',
  data () {
    return {
      persons: [
        {id: 1, name: 'Tom', age: 13},
        {id: 2, name: 'Jack', age: 12},
        {id: 3, name: 'Bob', age: 11},
      ]
    }
  },

  methods: {
    add() {
      this.persons.unshift({id: this.persons.length + 1, name: 'Bob', age: 14})
    },

    remove() {
      this.persons.shift()
    },

    sort() {
      this.persons.sort((p1, p2) => p1.age - p2.age)
    }
  }
}

/* 

const obj = {}
obj.name = "tom"

模板 => 虚拟DOM/Node => 真实DOM/Node


虚拟DOM/Node是什么:    vDom/ vNode
  一个较轻的普通JS对象    
     较轻  ==> 真实DOM是一个较重的对象   => 轻重看对象内部属性多少
     普通 ==> 真实DOM一旦更新了, 界面就会更新
  虚拟DOM对象有时也称为虚拟节点对象(vNode), 它包含了用于生成一个真实DOM/Node的必要信息, 比如:
    <ul id="list" name="xxx">
      <li>abc1</li>
      <li>abc2</li>
    </ul>
    
    {
      tagName: 'ul',
      props: {
        id: 'list',
        name: 'xxx'
      },
      children: [
        {
          tagName: 'li',
          props: {},
          children: ['abc1'],
          key: "0"
        },
        {
          tagName: 'li',
          props: {},
          children: ['abc2'],
          key: "1"
        },
      ],
      key: 0
    }
    
DOM Diff算法
  目标: 比较的结果是要确定: 哪些真实DOM可以复用(但内部内容可能要更新), 要创建哪些真实DOM
  1. 只做同层比较 => 虚拟DOM也是一个倒立树状态结构, 只进行同层比较, 这样比较次数少,效率高
  2. 找相同key虚拟节点
    如果没有找到, 直接创建新的真实DOM
    如果找到了, 进入下一步
  3. 先比较标签名
    如果不同, 直接创建新的真实DOM
    如果相同, 复用原来对应的真实DOM => 如果数据内容有变化, 更新真实DOM内部内容

  同层比较
  看key 
    没有: 新建
    有: 进入下一步
  看标签名
    不同: 新建
    相同: 复用
  看数据是否变化
    没变: 直接使用
    变了: 更新DOM中的内容

  虚拟DOM => DOM Diff => v-for的key  ==> 效率/效果问题
*/
</script>

<style scoped>

</style>
